//
//  main.m
//  08-iOS 9新特性
//
//  Created by LL.F on 16/7/13.
//  Copyright © 2016年 LL.F. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Truck.h"
#import "Student.h"

int main(int argc, const char * argv[]) {
    
    /*
     *  iOS 9 新特性(nullability, 泛型(genirics), _kindof)
     */
    
    // 1. nullability(是在编译层次上加了一些改动)
    // <1> nonnull: 用nonnull修饰的属性, 该属性的setter和getter方法都不能为空
    // nonnull 修饰的属性, 对应的setter和getter方法
    Person *per = [[Person alloc] init];
    //per.name = nil;// 因为要求非空, 所以出黄色警告
    per.name = @"张三";
    NSString *str = nil;
    per.name = str;//通过字符串间接赋空值,无警告提示,即可以为空
   
    // <2> nullable: 用nullable修饰的属性, 该属性的setter和getter方法都可以为空
    //  per setGender:<#(NSString * _Nullable)#>  //可以为空
    // [per gender];
    per.gender = nil;
    
    // <3> null_resettable: 用null_resettable修饰的属性, setter方法可以为空,     getter不可以
    //[per setFriend:(NSArray * _Nullable)];// setter方法可以为空
    [per friend];
    //per setDictionary:<#(NSDictionary * _Nonnull)#>
    
    // 2.泛型
    // 1) 带泛型的容器(规定了容器中所存储的类型)
    // <1> 带泛型的数组
    // 声明一个可变数组, <NSString *> 代表内部存放的都是字符串类型, 若有不是字符串类型就会报黄警告, 数组取出来的元素类型不再是id类型, 而是对应变成声明时的类型
    NSMutableArray<NSString *> *array = [NSMutableArray arrayWithObjects:@"123", nil];
    NSMutableArray *arr = [NSMutableArray array];
    NSString *string = array.firstObject;
    //array addObject:<#(nonnull NSString *)#>
    //arr addObject:<#(nonnull id)#>
    array.firstObject.length;//firstObject是字符串类型可以有.length
    //arr.firstObject.length;//这里firstobject是id类型,没有length属性
    [array addObject:@""];
    [array addObject:per];// 限定字符串类型后, 对象的类型就会有警告
    [array addObject:@20];
    NSInteger integer = [array objectAtIndex:0].length;
    // <2> 带泛型的字典: <NSString *, NSString *>
    NSMutableDictionary<NSString *, NSString *> *dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"yuhan", @"name", nil];
    [dic objectForKey:@"name"].length;
    //dic objectForKey:<#(nonnull NSString *)#>
    //dic setObject:<#(nonnull NSString *)#> forKey:<#(nonnull id<NSCopying>)#>
    
    // 2) 自定义泛型
    //想实现效果 一辆车里只能放学生, 一辆车里只能放人
    Truck *truck = [[Truck alloc] init];
    //truck addObject:<#(id)#>// id类型
    Truck<Person *> *truckPer = [[Truck alloc] init];
    //truckPer addObject:<#(Person *)#>// 加了<Person *> 就限定了Person类型
    [truckPer addObject:per];// per是person类型 没毛病
    //[truckPer addObject:array];// array不是Person类型 有警告
    
    Student *student = [[Student alloc] init];
    student.stuNumber = @"112";
    [truck addObject:student];
    Truck<Student *> *truckStu = [[Truck alloc] init];
    [truckStu addObject:student];
    [truckStu addObject:per];// 只能放学生 不能放人

    // 3). 协变性与逆变性
    Truck *newTruck = nil;
    Truck<NSArray *> *truck1 = nil;//(父类)
    Truck<NSMutableArray *> *truck2 = nil;//(子类)
    
    newTruck = truck1;
    newTruck = truck2;
    truck1 = newTruck;
    truck2 = newTruck;
    
    // 解决下面问题就要用协变与逆变(Truck类)
    // 协变性(目前在数组中有应用) __covariant 就是子类在给父类赋值时 自动强转
    // Truck<NSArray *> 与 Truck<NSMutableArray *>
    truck1 = truck2;//添加了__covariant后 警告消失
    // 逆变性(暂时不知道应用何处) __contravariant 就是父类给子类赋值, 自动强转
    truck2 = truck1;// 添加了 __contravariant警告就会消失
    
    // 3. __kindof : 一般应用在方法的返回值
    // __kindof 作用:加上__kindof修饰后, 返回的类型及其子类型都可以接收这个返回值
    Truck *truckKindof = [[Truck alloc] init];
    NSMutableArray *truckMutableArray = (NSMutableArray *)[truckKindof returnArray];//这个方法定义的返回值是NSArray, 所以需要强转解决类型不匹配问题
    NSMutableArray *newMutableArray = [truckKindof returnArray];// 加了__kindof就不用进行强转了
    NSArray *truckArray = [truckKindof returnArray];
    
    return 0;
}
